package com.nageoffer.jobtrain.projects.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nageoffer.jobtrain.common.web.exception.ClientException;
import com.nageoffer.jobtrain.common.web.result.Result;
import com.nageoffer.jobtrain.projects.common.biz.user.UserContext;
import com.nageoffer.jobtrain.projects.common.enums.SubmitCodeTypeEnums;
import com.nageoffer.jobtrain.projects.common.enums.UserPassOjQuestionEnums;
import com.nageoffer.jobtrain.projects.dao.entity.OjQuestionDO;
import com.nageoffer.jobtrain.projects.dao.entity.OjQuestionESDO;
import com.nageoffer.jobtrain.projects.dao.entity.UserAnswerDO;
import com.nageoffer.jobtrain.projects.dao.mapper.ContestAnswerMapper;
import com.nageoffer.jobtrain.projects.dao.mapper.OjQuestionMapper;
import com.nageoffer.jobtrain.projects.dao.mapper.UserAnswerMapper;
import com.nageoffer.jobtrain.projects.dto.req.OjQuestionExecuteResultReqDTO;
import com.nageoffer.jobtrain.projects.dto.req.OjQuestionPageListReqDTO;
import com.nageoffer.jobtrain.projects.dto.req.OjQuestionSubmitReqDTO;
import com.nageoffer.jobtrain.projects.dto.resp.*;
import com.nageoffer.jobtrain.projects.elasticsearch.OjQuestionRepository;
import com.nageoffer.jobtrain.projects.service.OjQuestionService;
import com.nageoffer.jobtrain.projects.service.handler.question.OjQuestionSubmitCodeByRocketMQTemplate;
import com.nageoffer.jobtrain.projects.service.handler.question.dto.OjQuestionPageDTO;
import com.nageoffer.jobtrain.projects.service.handler.question.dto.OjQuestionSubmitCaseDTO;
import com.nageoffer.jobtrain.projects.service.handler.question.dto.OjQuestionSubmitCodeTemplateDTO;
import com.nageoffer.jobtrain.projects.service.handler.question.dto.OjQuestionSubmitDTO;
import com.nageoffer.jobtrain.projects.service.handler.question.impl.OjQuestionSubmitCodeTypeJavaTemplate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * author：唐晨
 */
@Service
@RequiredArgsConstructor
public class OjQuestionServiceImpl extends ServiceImpl<OjQuestionMapper, OjQuestionDO> implements OjQuestionService {
    private final OjQuestionRepository ojQuestionRepository;
    private final OjQuestionMapper ojQuestionMapper;
    private final UserAnswerMapper userAnswerMapper;
    private final ContestAnswerMapper contestAnswerMapper;
    private final OjQuestionSubmitCodeTypeJavaTemplate ojQuestionSubmitCodeTypeJavaTemplate;
    private final OjQuestionSubmitCodeByRocketMQTemplate ojQuestionSubmitCodeByRocketMQTemplate;

    @Override
    public OjQuestionPageListRespDTO ojQuestionPageList(OjQuestionPageListReqDTO requestParam) {
        long totalCount = ojQuestionRepository.count();
        if (totalCount <= 0) {
            // 如果ES中查询不到任务数据，重新拉取数据库中的OJ题目数据，缓存到ES中
            handlerRefreshAllOjQuestion();
        }
        Sort sortType = Sort.by(Sort.Direction.DESC, "createTime");
        PageRequest pageable = PageRequest.of(requestParam.getCurrent() - 1, requestParam.getSize(), sortType);

        // 判断传参匹配关键字、题目难度是否为空
        Integer difficulty = requestParam.getDifficulty();
        String keyWord = requestParam.getKeyWord();
        Page<OjQuestionESDO> ojQuestionRepositoryResult;
        if (difficulty == null && StrUtil.isEmpty(keyWord)) {
            // 查询全部的OJ题目
            ojQuestionRepositoryResult = ojQuestionRepository.findAll(pageable);
        } else if (difficulty != null && StrUtil.isEmpty(keyWord)) {
            ojQuestionRepositoryResult = ojQuestionRepository.findOjQuestionByDifficulty(difficulty, pageable);
        } else if (difficulty == null && StrUtil.isNotEmpty(keyWord)) {
            ojQuestionRepositoryResult = ojQuestionRepository.findByTitleOrContent(keyWord, keyWord, pageable);
        } else {
            ojQuestionRepositoryResult = ojQuestionRepository.findByTitleOrContentAndDifficulty(keyWord, keyWord, difficulty, pageable);
        }
        long total = ojQuestionRepositoryResult.getTotalElements();
        if (total <= 0) {
            return OjQuestionPageListRespDTO.builder()
                    .totalSize(0L)
                    .build();
        }
        List<OjQuestionESDO> ojQuestionESList = ojQuestionRepositoryResult.getContent();
        List<OjQuestionPageDTO> actualOjQuestionList = BeanUtil.copyToList(ojQuestionESList, OjQuestionPageDTO.class);
        return OjQuestionPageListRespDTO.builder()
                .ojQuestionResult(actualOjQuestionList)
                .totalSize(total)
                .build();
    }

    // 除了查询ES数据为 null，将数据加载到 ES中
    private void handlerRefreshAllOjQuestion() {
        LambdaQueryWrapper<OjQuestionDO> queryWrapper = Wrappers.lambdaQuery(OjQuestionDO.class)
                .eq(OjQuestionDO::getDelFlag, 0);
        List<OjQuestionDO> ojQuestionList = ojQuestionMapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(ojQuestionList)) {
            return;
        }
        List<Long> passOjQuestionList = ojQuestionUserPassList();
        List<OjQuestionESDO> ojQuestionESList = BeanUtil.copyToList(ojQuestionList, OjQuestionESDO.class);
        ojQuestionESList.forEach(each -> {
            if (passOjQuestionList.contains(each.getId())) {
                each.setPass(UserPassOjQuestionEnums.PASSING.getType());
            }
        });
        ojQuestionRepository.saveAll(ojQuestionESList);
    }

    @Override
    public OjQuestionDetailRespDTO ojQuestionDetail(Long id) {
        // 先查询ES中是否存在该题目详情
        OjQuestionESDO ojQuestionESDO = ojQuestionRepository.findById(id).orElse(null);
        OjQuestionDetailRespDTO actualRespDTO = new OjQuestionDetailRespDTO();
        if (ojQuestionESDO != null) {
            BeanUtil.copyProperties(ojQuestionESDO, actualRespDTO);
            return actualRespDTO;
        }
        // 如果ES中不存在该OJ题目，查询数据库，并刷新到ES中
        LambdaQueryWrapper<OjQuestionDO> queryWrapper = Wrappers.lambdaQuery(OjQuestionDO.class)
                .eq(OjQuestionDO::getId, id)
                .eq(OjQuestionDO::getDelFlag, 0);
        OjQuestionDO ojQuestionDO = ojQuestionMapper.selectOne(queryWrapper);
        if (ojQuestionDO == null) {
            throw new ClientException("不存在该OJ题目详情");
        }
        handlerRefreshOneOjQuestion(ojQuestionDO);
        BeanUtil.copyProperties(ojQuestionDO, actualRespDTO);
        return actualRespDTO;
    }

    // 将某道OJ题目刷新到ES中
    private void handlerRefreshOneOjQuestion(OjQuestionDO ojQuestionDO) {
        OjQuestionESDO ojQuestionESDO = BeanUtil.toBean(ojQuestionDO, OjQuestionESDO.class);
        ojQuestionRepository.save(ojQuestionESDO);
    }

    // 获取ES中OJ题目列表详情
    private List<OjQuestionESDO> handlerGetESOjQuestionList() {
        Sort sortType = Sort.by(Sort.Direction.DESC, "createTime");
        Iterable<OjQuestionESDO> ojQuestionRepositoryAll = ojQuestionRepository.findAll(sortType);
        List<OjQuestionESDO> result = new ArrayList<>();
        ojQuestionRepositoryAll.forEach(result::add);
        return result;
    }

    @Override
    public OjQuestionPrevDetailRespDTO ojQuestionPrevDetail(Long id) {
        List<OjQuestionESDO> result = handlerGetESOjQuestionList();
        // 查找指定 id 对应的下标
        int currentIndex = -1;
        for (int i = 0; i < result.size(); i++) {
            if (result.get(i).getId().equals(id)) {
                currentIndex = i;
                break;
            }
        }
        // 如果未找到对应的 id，抛出异常或者返回 null
        if (currentIndex == -1) {
            throw new ClientException("未获取到当前OJ题目详情信息");
        }
        // 如果当前是第一题，则切换到最后一题
        Long prevId = currentIndex == 0
                ? result.get(result.size() - 1).getId() // 切换到最后一题
                : result.get(currentIndex - 1).getId(); // 获取上一题的 ID
        // 构造返回结果
        return OjQuestionPrevDetailRespDTO.builder()
                .id(prevId)
                .build();
    }

    @Override
    public OjQuestionNextDetailRespDTO ojQuestionNextDetail(Long id) {
        List<OjQuestionESDO> result = handlerGetESOjQuestionList();
        // 查找指定 id 对应的下标
        int currentIndex = -1;
        for (int i = 0; i < result.size(); i++) {
            if (result.get(i).getId().equals(id)) {
                currentIndex = i;
                break;
            }
        }
        // 如果未找到对应的 id，抛出异常或者返回 null
        if (currentIndex == -1) {
            throw new ClientException("未获取到当前OJ题目详情信息");
        }
        // 如果当前是最后一题，则切换到第一题
        Long nextId = currentIndex == result.size() - 1
                ? result.get(0).getId() // 切换到第一题
                : result.get(currentIndex + 1).getId(); // 获取下一题的 ID
        // 构造返回结果
        return OjQuestionNextDetailRespDTO.builder()
                .id(nextId)
                .build();
    }

    @Override
    public Result<OjQuestionSubmitRespDTO> ojQuestionSubmitCode(OjQuestionSubmitReqDTO requestParam) {
        switch (Objects.requireNonNull(SubmitCodeTypeEnums.getByType(requestParam.getCodeType()))) {
            case JAVA -> {
                return ojQuestionSubmitCodeTypeJavaTemplate.submitCode(BeanUtil.toBean(requestParam, OjQuestionSubmitCodeTemplateDTO.class));
            }
            case CPP -> {
                // 后续为不同提交代码类型留出扩展
            }
            default -> {
            }
        }
        return null;
    }

    // 用户提交判题代码 - by rocketmq
    @Override
    public Void ojQuestionSubmitCodeByRocketMQ(OjQuestionSubmitReqDTO requestParam) {
        switch (Objects.requireNonNull(SubmitCodeTypeEnums.getByType(requestParam.getCodeType()))) {
            case JAVA -> {
                ojQuestionSubmitCodeByRocketMQTemplate.submitCodeByRocketMQ(BeanUtil.toBean(requestParam, OjQuestionSubmitCodeTemplateDTO.class));
            }
            case CPP -> {
                // 后续为不同提交代码类型留出扩展
            }
            default -> {
            }
        }
        return null;
    }

    @Override
    public OjQuestionSubmitRespDTO ojQuestionExecuteResult(OjQuestionExecuteResultReqDTO requestParam) {
        // 根据竞赛ID是否为 null进行判断
        OjQuestionSubmitDTO executeResult;
        if (ObjectUtil.isNotNull(requestParam.getContestId())) {
            // 从用户竞赛记录表中进行获取判题结果
            executeResult = contestAnswerMapper.getExecuteResult(requestParam.getContestId(), requestParam.getQuestionId(),
                    UserContext.getUsername(), requestParam.getCodeType(), requestParam.getCurrentTime());
        } else {
            executeResult = userAnswerMapper.getExecuteResult(requestParam.getQuestionId(), UserContext.getUsername(),
                    requestParam.getCodeType(), requestParam.getCurrentTime());
        }
        OjQuestionSubmitRespDTO respDTO = convertToRespDTO(executeResult);
        if (respDTO != null && ObjectUtil.isNotNull(respDTO.getPass()) && ObjectUtil.equal(UserPassOjQuestionEnums.PASSING.getType(), respDTO.getPass())) {
            ojQuestionRepository.findById(respDTO.getQuestionId())
                    .ifPresent(ojQuestionESDO -> {
                        // 检查是否需要更新，避免不必要的 save 调用
                        if (!ObjectUtil.equal(UserPassOjQuestionEnums.PASSING.getType(), ojQuestionESDO.getPass())) {
                            ojQuestionESDO.setPass(UserPassOjQuestionEnums.PASSING.getType());
                            ojQuestionRepository.save(ojQuestionESDO);
                        }
                    });
        }
        return respDTO;
    }

    public OjQuestionSubmitRespDTO convertToRespDTO(OjQuestionSubmitDTO executeResult) {
        if (executeResult == null) {
            return null;
        }
        // 将字符串解析为 List<OjQuestionSubmitCaseDTO>
        List<OjQuestionSubmitCaseDTO> caseDTOList;
        try {
            caseDTOList = JSON.parseObject(
                    executeResult.getExecuteOutputCaseResult(),
                    new TypeReference<List<OjQuestionSubmitCaseDTO>>() {
                    }
            );
        } catch (Throwable ex) {
            // 如果解析失败，可以返回一个空列表或者抛出异常，取决于业务需求
            caseDTOList = Collections.emptyList();
        }
        // 构建 OjQuestionSubmitRespDTO
        return OjQuestionSubmitRespDTO.builder()
                .questionId(executeResult.getQuestionId())
                .pass(executeResult.getPass())
                .percent(executeResult.getPercent())
                .executeMessage(executeResult.getExecuteMessage())
                .executeOutputCaseResult(caseDTOList)
                .build();
    }

    @Override
    public List<String> ojQuestionHotList() {
        List<String> result = new ArrayList<>();
        List<Long> ojQuestionIdList = userAnswerMapper.getHotList();
        ojQuestionIdList.forEach(each -> {
            LambdaQueryWrapper<OjQuestionDO> queryWrapper = Wrappers.lambdaQuery(OjQuestionDO.class)
                    .eq(OjQuestionDO::getId, each)
                    .eq(OjQuestionDO::getDelFlag, 0);
            OjQuestionDO ojQuestionDO = ojQuestionMapper.selectOne(queryWrapper);
            result.add(ojQuestionDO.getTitle());
        });
        return result;
    }

    @Override
    public List<Long> ojQuestionUserPassList() {
        LambdaQueryWrapper<UserAnswerDO> queryWrapper = Wrappers.lambdaQuery(UserAnswerDO.class)
                .eq(UserAnswerDO::getPhoneNumber, UserContext.getUsername())
                .eq(UserAnswerDO::getDelFlag, 0);
        List<UserAnswerDO> userAnswerList = userAnswerMapper.selectList(queryWrapper);
        return userAnswerList.stream()
                .map(UserAnswerDO::getQuestionId)
                .collect(Collectors.toList());
    }
}
